package main

import (
	"fmt"
	"reflect"
)

// 通过反射调用一系列对应的方法
// 定义一个结构体
type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"monster_age"`
	Score float32
	Sex   string
}

// 方法1，显示s的值
func (s Monster) Print() {
	fmt.Println("----")
	fmt.Println(s)
	fmt.Println("----")
}

// 方法2，两数和
func (s Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}

// 方法3，接收四个值，给s赋值
func (s Monster) Set(name string, age int, score float32, sex string) {
	s.Name = name
	s.Age = age
	s.Score = score
	s.Sex = sex
}

func TestStruct(a interface{}) {
	// 获取reflect.Type 类型
	typ := reflect.TypeOf(a)
	// 获取reflect.Value 类型
	val := reflect.ValueOf(a)
	// 获取a对应类型
	kd := val.Kind()
	// 判断传入参数是否是结构体
	if kd != reflect.Struct {
		fmt.Println("expect struct")
		return
	}
	// 获取传入参数的所有字段数量
	num := val.NumField()
	fmt.Printf("struct has %d fields\n", num) //4
	for i := 0; i < num; i++ {
		// 遍历结构体所有字段
		fmt.Printf("Field %d:值为=%v\n", i, val.Field(i)) //可以使用类型断言转成对应类型进行操作
		// 获取参数标签，注意需要通过reflect.Type来获取tag标签的值
		tagVal := typ.Field(i).Tag.Get("json")
		// 如果该字段有tag标签就显示，否则不显示
		if tagVal != "" {
			fmt.Printf("Field %d: tag为%v\n", i, tagVal)
		}
	}
	// 获取该变量有多少个方法
	numOfMethod := val.NumMethod()
	fmt.Printf("struct has %d method\n", numOfMethod) // 3
	// 获取第二个方法，并且调用它传入nil
	// 这里调用方法会有一个原则，它会根据函数名ASCII码进行重排序，跟函数的位置无关
	val.Method(1).Call(nil)

	// 调用结构体的第一个方法Method(0)
	// 因为调用参与反射参数的方法使用Call(),这个方法的传入参数是[]Value 返回参数是[]Value
	var params []reflect.Value                   // 声明一个rValue切片
	params = append(params, reflect.ValueOf(10)) // 将传入参数转成rValue
	params = append(params, reflect.ValueOf(20))
	res := val.Method(0).Call(params) // 传入的参数是[]reflect.Value类型
	fmt.Println(res[0].Int())         // 返回的参数是[]reflect.Value类型
}

func TestStruct1(a interface{}) {
	println("------")
	// 获取reflect.Type 类型
	typ := reflect.TypeOf(a)
	// 获取reflect.Value 类型
	val := reflect.ValueOf(a)
	// 获取a对应类型
	kd := val.Kind()
	// 判断传入参数是否是结构体
	if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct {
		fmt.Println("expect struct")
		return
	}
	// 获取传入参数的所有字段数量
	num := val.Elem().NumField()
	val.Elem().Field(0).SetString("白象精")
	fmt.Printf("struct has %d fields\n", num) //4
	for i := 0; i < num; i++ {
		// 遍历结构体所有字段
		fmt.Printf("Field %d:值为=%v\n", i, val.Elem().Field(i)) //可以使用类型断言转成对应类型进行操作
		// 获取参数标签，注意需要通过reflect.Type来获取tag标签的值
		tagVal := typ.Elem().Field(i).Tag.Get("json")
		// 如果该字段有tag标签就显示，否则不显示
		if tagVal != "" {
			fmt.Printf("Field %d: tag为%v\n", i, tagVal)
		}
		fmt.Printf("%d%v\n", i, val.Elem().Field(i).Kind()) //可以使用类型断言转成对应类型进行操作

	}
	// 获取该变量有多少个方法
	numOfMethod := val.Elem().NumMethod()
	fmt.Printf("struct has %d method\n", numOfMethod) // 3
	// 获取第二个方法，并且调用它传入nil
	// 这里调用方法会有一个原则，它会根据函数名ASCII码进行重排序，跟函数的位置无关
	val.Elem().Method(1).Call(nil)
}

func main() {
	// 创建一个结构体实例
	var m = Monster{
		Name:  "黄鼠狼精",
		Age:   400,
		Score: 38.2,
	}
	fmt.Println(m)
	TestStruct(m)
	TestStruct1(&m)
	fmt.Println(m)
}
